package com.wxmit.module.system.service;

import cn.dev33.satoken.temp.SaTempUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.github.hiwepy.ip2region.spring.boot.IP2regionTemplate;
import com.github.hiwepy.ip2region.spring.boot.ext.RegionAddress;
import com.wxmit.base.constant.ShushanConstants;
import com.wxmit.base.enums.BusinessStatus;
import com.wxmit.base.enums.YesOrNoEnum;
import com.wxmit.base.exception.UserBanException;
import com.wxmit.base.exception.UserLoginErrorException;
import com.wxmit.base.util.ServletUtils;
import com.wxmit.base.util.StringUtils;
import com.wxmit.framework.satoken.core.util.RsaUtil;
import com.wxmit.framework.satoken.core.util.StpUserUtil;
import com.wxmit.module.system.domain.SysRole;
import com.wxmit.module.system.domain.SysUser;
import com.wxmit.module.system.domain.bo.LoginBody;
import com.wxmit.module.system.domain.vo.LoginTokenVo;
import com.wxmit.module.system.enums.SysUserLoginErrorEnum;
import com.wxmit.starter.cache.api.CacheApi;
import com.wxmit.system.domain.LoginUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.crypto.BadPaddingException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description SysLoginService
 * @Author huangxiong
 * @Date 2022/7/11 11:11
 * @Version V1.0
 */
@Service
@Slf4j
public class SysLoginService {

    @Autowired
    private ISysUserService sysUserService;

    @Autowired
    private ISysLoginLogService sysLoginLogService;

    @Autowired
    private IP2regionTemplate ip2regionTemplate;

    @Autowired
    private CacheApi cacheApi;

    /**
     *  30天
     */
    private final Long REFRESH_TOKEN_TIMEOUT = 60*60*24*30L;

    /**
     *  登陆接口
     * @param loginBody 登陆实体
     * @return token
     */
    public LoginTokenVo login(LoginBody loginBody){

        //校验验证码
        checkVerify(loginBody.getCode(), loginBody.getUuid());
        //查询数据库
        SysUser sysUser = sysUserService.getByUserName(loginBody.getUserName());
        checkLogin(sysUser,loginBody);

        LoginUser loginUser = buildLoginUser(sysUser);
        String refreshToken = "";
        if(loginBody.isRememberMe()){
            refreshToken = SaTempUtil.createToken(sysUser.getUserName(), REFRESH_TOKEN_TIMEOUT);
            loginUser.setRefreshToken(refreshToken);
        }
        String token = StpUserUtil.loginWithLoginUser(loginUser);


        sysLoginLogService.saveWithAsync(loginUser, BusinessStatus.SUCCESS,"登陆成功");
        sysUserService.updateLoginStatus(sysUser.getId(), ServletUtils.getClientIP(),new Date());
        return new LoginTokenVo(token,refreshToken);
    }

    public LoginTokenVo loginByRefreshToken(String refreshToken){
        Assert.notNull(refreshToken,"当前token已过期，请重新登录");
        String userName =(String) SaTempUtil.parseToken(refreshToken);
        Assert.notNull(userName,"当前token已过期，请重新登录");
        SysUser sysUser = sysUserService.getByUserName(userName);

        LoginUser loginUser = buildLoginUser(sysUser);
        String newRefreshToken = SaTempUtil.createToken(sysUser.getUserName(), REFRESH_TOKEN_TIMEOUT);
        loginUser.setRefreshToken(refreshToken);
        String token = StpUserUtil.loginWithLoginUser(loginUser);
        SaTempUtil.deleteToken(refreshToken);
        return new LoginTokenVo(token,newRefreshToken);
    }

    public SysUser getSysUserInfo(){
      return StpUserUtil.getUser(SysUser.class);
    }

    private LoginUser buildLoginUser(SysUser user){
        LoginUser loginUser = new LoginUser();
        loginUser.setUserObj(user);
        loginUser.setUserType(StpUserUtil.DEF_USER_TYPE);
        loginUser.setId(user.getId());
        loginUser.setUsername(user.getUserName());
        loginUser.setDeptId(user.getDeptId());
        String clientIP = ServletUtils.getClientIP();
        RegionAddress r = ip2regionTemplate.getRegionAddress(clientIP);
        UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getUserAgent());
        loginUser.setLoginLocation(
                StringUtils.format("{}{}{}({})",
                        getIfNotEq(r.getCountry(),"0",""),
                        getIfNotEq(r.getProvince(),"0",""),
                        getIfNotEq(r.getCity(),"0",""),
                        r.getISP())
        );
        loginUser.setBrowser(userAgent.getBrowser().getName());
        loginUser.setOs(userAgent.getOs().getName());
        loginUser.setIpaddr(clientIP);
        loginUser.setLoginTime(System.currentTimeMillis());


        List<SysRole> roles = user.getRoles();
        List<String> roleKeys = Collections.emptyList();
        if(!CollectionUtils.isEmpty(roles)){
            roleKeys = roles.stream().map(SysRole::getRoleKey).collect(Collectors.toList());
        }
        loginUser.setRoleKeys(roleKeys);
        return loginUser;
    }

    private void checkLogin(SysUser sysUser,LoginBody loginBody){
        // 找不到用户
        if(Objects.isNull(sysUser)){
            throwLoginError(null,SysUserLoginErrorEnum.USER_NOT_FIND,false);
        }

        //用户被封禁
        if(YesOrNoEnum.YES.eqCode(sysUser.getBan())){
            throwLoginError(sysUser,SysUserLoginErrorEnum.USER_LOCK,true);
        }

        // 密码不正确
        Boolean eqPassword = false;
        try {
            eqPassword = StpUserUtil.eqPassword(sysUser.getPassword(), RsaUtil.decryptByPrivateKey(loginBody.getPassword()));
        }catch (Exception e){
            //do nothing
            eqPassword = false;
        }
        if(!eqPassword){
            //存入USER封禁列表
            sysUserService.increaseLoginErr(sysUser.getId());
            throwLoginError(sysUser,SysUserLoginErrorEnum.PS_ERROR,true);
        }

        // 用户被禁用
        if(YesOrNoEnum.NO.eqCode(sysUser.getStatus())){
            throwLoginError(sysUser,SysUserLoginErrorEnum.USER_DISABLED,false);
        }
    }

    /**
     *  抛出登陆异常信息
     * @param sysUser 登陆用户
     * @param errorEnum 异常类型
     * @param saveLog 是否保存日志
     */
    private void throwLoginError(SysUser sysUser,SysUserLoginErrorEnum errorEnum,Boolean saveLog){
        if(saveLog){
            sysLoginLogService.saveWithAsync(buildLoginUser(sysUser),BusinessStatus.FAIL,errorEnum.serverLog);
        }
        switch (errorEnum){
            case USER_DISABLED:
                throw  new UserBanException(errorEnum.clientLog);
            default:
                throw  new UserLoginErrorException(errorEnum.clientLog);
        }

    }

    /**
     *  如果不等于 eqVal 则返回val， 相等则返回df
     * @param val 数值
     * @param eqVal 相等值
     * @param df 默认值
     * @return 结果
     */
    private String getIfNotEq(String val,String eqVal,String df){
        return Objects.equals(val,eqVal)?df:val;
    }

    private void checkVerify(String code, String uuid) {
        Assert.isTrue(ObjectUtils.isNotEmpty(code), "验证码不能为空!");
        String verifyKey = ShushanConstants.CAPTCHA_CODE_KEY + uuid;
        Optional<String> captchaOpt = cacheApi.get(verifyKey);
        cacheApi.del(verifyKey);
        Assert.isTrue(captchaOpt.isPresent(), "验证码已失效!");
        Assert.isTrue(code.equalsIgnoreCase(captchaOpt.get()), "验证码错误!");

    }
}
